Skip to content

Unify module splitting implementation for JSPI#26396

Merged
brendandahl merged 4 commits intoemscripten-core:mainfrom
brendandahl:split-module-embind-jspi
Mar 20, 2026
Merged

Unify module splitting implementation for JSPI#26396
brendandahl merged 4 commits intoemscripten-core:mainfrom
brendandahl:split-module-embind-jspi

Conversation

@brendandahl
Copy link
Collaborator

Previously, module splitting with JSPI required a special built-in function __load_secondary_module and wasm-split setting to coordinate the loading of the secondary module.

This change updates the splitModuleProxyHandler to support JSPI by wrapping the generated placeholder functions in WebAssembly.Suspending. The placeholder will then automatically load the secondary module asynchronously.

A standalone save_profile_data.js script is extracted from existing tests for reuse, and a new test test_split_module_embind_jspi is added to ensure module splitting functions correctly alongside embind and JSPI.

Previously, module splitting with JSPI required a special built-in
function `__load_secondary_module` and wasm-split setting to coordinate
the loading of the secondary module.

This change updates the `splitModuleProxyHandler` to support JSPI
by wrapping the generated placeholder functions in
`WebAssembly.Suspending`. The placeholder will then automatically load
the secondary module asynchronously.

A standalone `save_profile_data.js` script is extracted from existing
tests for reuse, and a new test `test_split_module_embind_jspi` is
added to ensure module splitting functions correctly alongside embind
and JSPI.
@brendandahl
Copy link
Collaborator Author

For some more context: The old version of JSPI required changes to wasm so this new approach wasn't possible.

Copy link
Member

@aheejin aheejin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay!

By the way, what made this possible? In other words, why did we have to resort to __load_secondary_module function before and not anymore?

@@ -0,0 +1,2 @@
deferred_function: [object Promise]
deferred_function await: 82 No newline at end of file
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No newline at the end of file

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And it's a little weird that we have this here as a file and we also check this in the code after the split:

    self.assertIn('primary_function: 42\n' +
                  'Custom handler for loading split module.\n' +
                  'deferred_function: [object Promise]\n' +
                  'deferred_function await: 82', result)

How about checking both of the cases (before and after the split) in code to be consistent? Also they have a slightly different outputs, so..

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved it all into the test now. The pre/post part should be the same now with only the middle being different.

@brendandahl
Copy link
Collaborator Author

By the way, what made this possible? In other words, why did we have to resort to __load_secondary_module function before and not anymore?

When JSPI was first introduced there was a concept of a "suspender object" that had to be passed around in wasm as an argument. To handle that we had to run a binaryen pass to modify the wasm and change the export/import signatures. I forget the details, but something about that caused issues with the proxy and replacing the function with a different signature.

Anyway, JSPI was changed to be fully on the JS side so nothing in Wasm needed to change. That made it easier to dynamically adjust what will have JSPI wrappers at runtime and just inject them in the proxy object that the original split used.

@brendandahl brendandahl requested a review from aheejin March 19, 2026 23:34
@brendandahl brendandahl merged commit 5ee7847 into emscripten-core:main Mar 20, 2026
38 checks passed
'--enable-mutable-globals', '--enable-bulk-memory', '--enable-nontrapping-float-to-int',
'--export-prefix=%', 'test_split_module.wasm.orig', '-o1', 'primary.wasm', '-o2', 'secondary.wasm', '--profile=profile.data']
if jspi:
wasm_split_run += ['--jspi', '--enable-reference-types']
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean we can delete this binaryen pass now too?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants